/**************************************************************************************************
* Title: SumColor_16f.inl
* Author: Gael Huber
* Description: Defines various functions pertaining to colors
**************************************************************************************************/

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
inline Color_16f::Color_16f(void)
	: v(_mm_setzero_ps())
{ }

/**************************************************************************************************
* Constructor given a vector
**************************************************************************************************/
inline Color_16f::Color_16f(const __m128& f)
	: v(f)
{ }

/**************************************************************************************************
* Constructor given an fully composed bit sequence
**************************************************************************************************/
inline Color_16f::Color_16f(unsigned int argb) {
	const float f(1.0f / 255.0f);
	r = f * (float)(unsigned char)(argb >> 16);
	g = f * (float)(unsigned char)(argb >> 8);
	b = f * (float)(unsigned char)(argb >> 0);
	a = f * (float)(unsigned char)(argb >> 24);
}

/**************************************************************************************************
* Constructor through array
**************************************************************************************************/
inline Color_16f::Color_16f(const float* f)
	: v(_mm_load_ps(f))
{ }

/**************************************************************************************************
* Constructor through specified values
**************************************************************************************************/
inline Color_16f::Color_16f(float r, float g, float b, float a)
	: v(_mm_set_ps(a, b, g, r))
{ }

/**************************************************************************************************
* Copy constructor
**************************************************************************************************/
inline Color_16f::Color_16f(const Color_16f& rhs)
	: v(rhs.v)
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
inline Color_16f::~Color_16f(void)
{ }

/**************************************************************************************************
* Return the data as an encoded int
**************************************************************************************************/
inline Color_16f::operator unsigned int(void) const {
	// We want to ensure color bounds are enforced as this does not take place during standard
	// mathematical procedures
	return	(r >= 1.0f ? 0xff : r <= 0.0f ? 0x00 : (unsigned int)(r * 255.0f + 0.5f) << 16) |
			(g >= 1.0f ? 0xff : g <= 0.0f ? 0x00 : (unsigned int)(g * 255.0f + 0.5f) << 8) |
			(b >= 1.0f ? 0xff : b <= 0.0f ? 0x00 : (unsigned int)(b * 255.0f + 0.5f) << 0) |
			(a >= 1.0f ? 0xff : a <= 0.0f ? 0x00 : (unsigned int)(a * 255.0f + 0.5f) << 24);

}

/**************************************************************************************************
* Return the data as a float array
**************************************************************************************************/
inline Color_16f::operator float*(void) {
	return (float*) this;
}

/**************************************************************************************************
* Return the data as a constant float array
**************************************************************************************************/
inline Color_16f::operator const float*(void) const {
	return (const float*) this;
}

/**************************************************************************************************
* Assignment operator
**************************************************************************************************/
inline Color_16f& Color_16f::operator=(const Color_16f& rhs) {
	if(this == &rhs)
		return *this;

	v = rhs.v;
	return *this;
}

/**************************************************************************************************
* Add a color and return the result
**************************************************************************************************/
inline Color_16f& Color_16f::operator+=(const Color_16f& rhs) {
	v = _mm_add_ps(v, rhs.v);
	return *this;
}

/**************************************************************************************************
* Subtract a color and return the result
**************************************************************************************************/
inline Color_16f& Color_16f::operator-=(const Color_16f& rhs) {
	v = _mm_sub_ps(v, rhs.v);
	return *this;
}

/**************************************************************************************************
* Multiply the color by a scalar value
**************************************************************************************************/
inline Color_16f& Color_16f::operator*=(float rhs) {
	v = _mm_mul_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Multiply the color by a vector value
**************************************************************************************************/
inline Color_16f& Color_16f::operator*=(const __m128& rhs) {
	v = _mm_mul_ps(v, rhs);
	return *this;
}

/**************************************************************************************************
* Divide the color by a scalar value
**************************************************************************************************/
inline Color_16f& Color_16f::operator/=(float rhs) {
	v = _mm_div_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Divide the color by a scalar value
**************************************************************************************************/
inline Color_16f& Color_16f::operator/=(const __m128& rhs) {
	v = _mm_div_ps(v, rhs);
	return *this;
}

/**************************************************************************************************
* Return an unmodified copy of the data
**************************************************************************************************/
inline Color_16f Color_16f::operator+(void) const {
	return *this;
}

/**************************************************************************************************
* Return a negated copy of the data
**************************************************************************************************/
inline Color_16f Color_16f::operator-(void) const {
	return Color_16f(_mm_mul_ps(v, _mm_set1_ps(-1.0f)));
}

/**************************************************************************************************
* Return the sum of two colors
**************************************************************************************************/
inline Color_16f Color_16f::operator+(const Color_16f& rhs) const {
	return Color_16f(_mm_add_ps(v, rhs.v));
}

/**************************************************************************************************
* Return the color representing the difference between two colors
**************************************************************************************************/
inline Color_16f Color_16f::operator-(const Color_16f& rhs) const {
	return Color_16f(_mm_sub_ps(v, rhs.v));
}

/**************************************************************************************************
* Multiply a color by a scalar value
**************************************************************************************************/
inline Color_16f Color_16f::operator*(float rhs) const {
	return Color_16f(_mm_mul_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Multiply a color by a vector value
**************************************************************************************************/
inline Color_16f Color_16f::operator*(const __m128& rhs) const {
	return Color_16f(_mm_mul_ps(v, rhs));
}

/**************************************************************************************************
* Divide the color by a scalar value
**************************************************************************************************/
inline Color_16f Color_16f::operator/(float rhs) const {
	return Color_16f(_mm_div_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Divide the color by a vector value
**************************************************************************************************/
inline Color_16f Color_16f::operator/(const __m128& rhs) const {
	return Color_16f(_mm_div_ps(v, rhs));
}

/**************************************************************************************************
* Multiply a color by a scalar value
**************************************************************************************************/
inline Color_16f operator*(float lhs, const Color_16f& rhs) {
	return Color_16f(_mm_mul_ps(_mm_load1_ps(&lhs), rhs.v));
}

/**************************************************************************************************
* Multiply a color by a vector value
**************************************************************************************************/
inline Color_16f operator*(const __m128& lhs, const Color_16f& rhs) {
	return Color_16f(_mm_mul_ps(lhs, rhs.v));
}

/**************************************************************************************************
* Equivalence operator
**************************************************************************************************/
inline bool Color_16f::operator==(const Color_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) == 0x0F;
}

/**************************************************************************************************
* Non-equivalence operator
**************************************************************************************************/
inline bool Color_16f::operator!=(const Color_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) != 0x0F;
}

/**************************************************************************************************
* Take the negative of the color. (1-r, 1-g, 1-b, a)
**************************************************************************************************/
inline Color_16f* ColorNegative_16f(Color_16f* pOut, const Color_16f* pC) {
	pOut->r = 1.0f - pC->r;
	pOut->g = 1.0f - pC->g;
	pOut->b = 1.0f - pC->b;
	pOut->a = pC->a;
	return pOut;
}

/**************************************************************************************************
* Sum of two colors
**************************************************************************************************/
inline Color_16f* ColorAdd_16f(Color_16f* pOut, const Color_16f* pC1, const Color_16f* pC2) {
	pOut->v = _mm_add_ps(pC1->v, pC2->v);
	return pOut;
}

/**************************************************************************************************
* Difference between two vectors
**************************************************************************************************/
inline Color_16f* ColorSub_16f(Color_16f* pOut, const Color_16f* pC1, const Color_16f* pC2) {
	pOut->v = _mm_sub_ps(pC1->v, pC2->v);
	return pOut;
}

/**************************************************************************************************
* Scale a color by a given factor
**************************************************************************************************/
inline Color_16f* ColorScale_16f(Color_16f* pOut, const Color_16f* pC, float s) {
	pOut->v = _mm_mul_ps(pC->v, _mm_load1_ps(&s));
	return pOut;
}

/**************************************************************************************************
* Scale a color by a given factor
**************************************************************************************************/
inline Color_16f* ColorScale_16f(Color_16f* pOut, const Color_16f* pC, const __m128& s) {
	pOut->v = _mm_mul_ps(pC->v, s);
	return pOut;
}

/**************************************************************************************************
* Modulate (r1*r2, g1*g2, b1*b2, a1*a2)
**************************************************************************************************/
inline Color_16f* ColorModulate_16f(Color_16f* pOut, const Color_16f* pC1, const Color_16f* pC2) {
	pOut->v = _mm_mul_ps(pC1->v, pC2->v);
}

/**************************************************************************************************
* LERP of r,g,b, and a. C1 + s(C2-C1)
**************************************************************************************************/
inline Color_16f* ColorLerp_16f(Color_16f* pOut, const Color_16f* pC1, const Color_16f* pC2, 
	float s) {
	pOut->v = _mm_add_ps(pC1->v, _mm_mul_ps(_mm_load1_ps(&s), _mm_sub_ps(pC2->v, pC1->v)));
	return pOut;
}

/**************************************************************************************************
* LERP of r,g,b, and a. C1 + s(C2-C1)
**************************************************************************************************/
inline Color_16f* ColorLerp_16f(Color_16f* pOut, const Color_16f* pC1, const Color_16f* pC2, 
	const __m128& s) {
	pOut->v = _mm_add_ps(pC1->v, _mm_mul_ps(s, _mm_sub_ps(pC2->v, pC1->v)));
	return pOut;
}